#ifndef CXX_STUDY_CONCURRENT_QUEUE_HPP
#define CXX_STUDY_CONCURRENT_QUEUE_HPP

#include <cstdlib>
#include <mutex>
#include <condition_variable>

namespace cxx_study {

  namespace concurrent {

    /**
     * 并发队列
     * */
    template<class _Tp>
    class queue {
    public:
      explicit queue(std::size_t max_size = 1024) : max_size_(max_size), size_(0) {
        data_ = (_Tp *) malloc(sizeof(_Tp) * max_size);
      }

      ~queue() {
        free(data_);
      }

      void push(const _Tp &value) {
        std::unique_lock<std::mutex> lock(mutex_);
        full_cv_.wait(lock, [&] { return size_ < max_size_; });

        data_[size_++] = value;
        empty_cv_.notify_one();
      }

      template<typename..._Args>
      void emplace(_Args &&... __args) {
        std::unique_lock<std::mutex> lock(mutex_);
        full_cv_.wait(lock, [&] { return size_ < max_size_; });

        new(&data_[size_++]) _Tp(std::forward<_Args>(__args)...);
        empty_cv_.notify_one();
      }

      const _Tp &pop() {
        std::unique_lock<std::mutex> lock(mutex_);
        empty_cv_.wait(lock, [&] { return size_ > 0; });

        auto &result = data_[--size_];
        full_cv_.notify_one();

        return result;
      }

      inline std::size_t size() const {
        return size_;
      }

    private:
      _Tp *data_;
      std::size_t max_size_;
      std::size_t size_;
      std::mutex mutex_;
      std::condition_variable empty_cv_, full_cv_;
    };
  }
}


#endif //CXX_STUDY_CONCURRENT_QUEUE_HPP
